/*
**    Copyright (C) 2003-2011 Institute for Systems Biology
**                            Seattle, Washington, USA.
**
**    This library is free software; you can redistribute it and/or
**    modify it under the terms of the GNU Lesser General Public
**    License as published by the Free Software Foundation; either
**    version 3 of the License, or (at your option) any later version.
**
**    This library is distributed in the hope that it will be useful,
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
**    Lesser General Public License for more details.
**
**    You should have received a copy of the GNU Lesser General Public
**    License along with this library; if not, write to the Free Software
**    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

package org.systemsbiology.util;

import java.io.Serializable;
import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * A specialized data structure for adding double values. Designed to minimize
 * error induced by double rounding, under the constraint that total() isn't
 * called until the absolute last possible moment. Unnecesary calls to total()
 * will cause double precision error to grow further than it needs to.
 */
public class TournamentAdder implements Summator {

	protected PriorityQueue<Double> group;
	
	public TournamentAdder(){
		group = new PriorityQueue<Double>(5, new NegativeInsensitiveComparator());
	}
	
	/* (non-Javadoc)
	 * @see org.systemsbiology.util.Summator#add(java.lang.Double)
	 */
	public void add(double d){
		group.add(d);
	}
	
	/* (non-Javadoc)
	 * @see org.systemsbiology.util.Summator#total()
	 */
	public double total(){
		if(group.isEmpty()){
			return 0;
		}
		while(group.size() > 1){
			double a = group.poll();
			double b = group.poll();
			group.add(a+b);
		}
		return group.element();
	}
	
	/* (non-Javadoc)
	 * @see org.systemsbiology.util.Summator#clear()
	 */
	public void clear(){
		group.clear();
	}
	
	private static class NegativeInsensitiveComparator implements Comparator<Double>, Serializable{
		private static final long serialVersionUID = 1477452322682310399L;
		private static int doublecomp(double a, double b){
			if(a == b){return 0;}
			if(a < b) {return -1;}
			if(a > b) {return 1;}
			return 0; //incomparable things like NaN, etc.
		}
		public int compare(Double o1, Double o2) {
			if (Math.abs(o1) != Math.abs(o2)){
				return doublecomp(Math.abs(o1), Math.abs(o2));
			}
			return doublecomp(o1, o2);
		}
		
	}
}
